package org.jboss.resteasy.springboot;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.jboss.resteasy.core.AsynchronousDispatcher;
import org.jboss.resteasy.core.ResourceMethodRegistry;
import org.jboss.resteasy.core.ResteasyContext;
import org.jboss.resteasy.core.SynchronousDispatcher;
import org.jboss.resteasy.plugins.server.servlet.ListenerBootstrap;
import org.jboss.resteasy.plugins.spring.SpringBeanProcessor;
import org.jboss.resteasy.spi.Dispatcher;
import org.jboss.resteasy.spi.Registry;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@AutoConfigureAfter({WebMvcAutoConfiguration.class})
@EnableConfigurationProperties
public class ResteasyAutoConfiguration {
    private static Logger logger = LoggerFactory.getLogger(ResteasyAutoConfiguration.class);

    public ResteasyAutoConfiguration() {
    }

    @Bean
    @Qualifier("ResteasyProviderFactory")
    public static BeanFactoryPostProcessor springBeanProcessor() {
        ResteasyProviderFactory resteasyProviderFactory = ResteasyProviderFactory.newInstance();
        ResourceMethodRegistry resourceMethodRegistry = new ResourceMethodRegistry(resteasyProviderFactory);
        SpringBeanProcessor springBeanProcessor = new SpringBeanProcessor();
        springBeanProcessor.setProviderFactory(resteasyProviderFactory);
        springBeanProcessor.setRegistry(resourceMethodRegistry);
        logger.debug("SpringBeanProcessor has been created");
        return springBeanProcessor;
    }

    @Bean
    public ServletContextListener resteasyBootstrapListener(@Qualifier("ResteasyProviderFactory") final BeanFactoryPostProcessor beanFactoryPostProcessor) {
        ServletContextListener servletContextListener = new ServletContextListener() {
            private SpringBeanProcessor springBeanProcessor = (SpringBeanProcessor)beanFactoryPostProcessor;
            protected ResteasyDeployment deployment;

            public void contextInitialized(ServletContextEvent sce) {
                ServletContext servletContext = sce.getServletContext();
                ResteasyContext.pushContext(ServletContext.class, servletContext);
                ListenerBootstrap config = new ListenerBootstrap(servletContext);
                ResteasyProviderFactory resteasyProviderFactory = this.springBeanProcessor.getProviderFactory();
                ResourceMethodRegistry resourceMethodRegistry = (ResourceMethodRegistry)this.springBeanProcessor.getRegistry();
                this.deployment = config.createDeployment();
                this.deployment.setProviderFactory(resteasyProviderFactory);
                this.deployment.setRegistry(resourceMethodRegistry);
                if (this.deployment.isAsyncJobServiceEnabled()) {
                    AsynchronousDispatcher dispatcher = new AsynchronousDispatcher(resteasyProviderFactory, resourceMethodRegistry);
                    this.deployment.setDispatcher(dispatcher);
                } else {
                    SynchronousDispatcher dispatcherx = new SynchronousDispatcher(resteasyProviderFactory, resourceMethodRegistry);
                    this.deployment.setDispatcher(dispatcherx);
                }

                this.deployment.start();
                servletContext.setAttribute(ResteasyProviderFactory.class.getName(), this.deployment.getProviderFactory());
                servletContext.setAttribute(Dispatcher.class.getName(), this.deployment.getDispatcher());
                servletContext.setAttribute(Registry.class.getName(), this.deployment.getRegistry());
            }

            public void contextDestroyed(ServletContextEvent sce) {
                try {
                    if (this.deployment != null) {
                        this.deployment.stop();
                    }
                } finally {
                    ResteasyContext.popContextData(ServletContext.class);
                }

            }
        };
        logger.debug("ServletContextListener has been created");
        return servletContextListener;
    }

    @Bean(
            name = {"JaxrsApplicationServletBuilder"}
    )
    public ResteasyApplicationBuilder resteasyApplicationBuilder() {
        return new ResteasyApplicationBuilder();
    }

    @Bean
    public static ResteasyEmbeddedServletInitializer resteasyEmbeddedServletInitializer() {
        return new ResteasyEmbeddedServletInitializer();
    }
}
